
# Hillbilly version of ns_session

ns_cache_create sessions 1024
ns_cache_create conns 1024

proc ns_session { command {args ""}} {

	# Expire sessions after 15 minutes of inactivity
	set expires 900
	# update expiration after each get.
	set touch 1
	# Persist sessions to disk
	set persist 1

	switch $command {

		exists {
			# Check for existence of session.  Sessions are 
			# said to exist if there is an entry in the cache
			# that has not expired with the given id.  They are
			# created by the create, put, or get commands.
			if {[length [ns_cache_keys sessions [ns_session id]]} {
				return 1
			} else {
				return 0
			}
		}

		list {
			# List all existing sessions
			return [ns_cache_keys sessions]
		}	
		
		id {
			# Get or set session id and return it
			# This needs to return the same id if called multiple times
			# by one conn.  TODO
			upvar #0 session_id session_id
			if {[info exists session_id]} {
				return $session_id
			}
			set session_id [ns_getcookie session_id 0]
			if {$session_id == 0} {
				set session_id [ns_sha1 [ns_conn peeraddr][random 100000]]
				ns_setcookie session_id $session_id
			}
			return $session_id
		}
		
		load {
			# Load session.  Also creates a session since it is called
			# from put and get.

			upvar sessiondata sessiondata
			if {[llength $args] > 0} {
				set session_id [lindex $args 0] 
			} else {
				set session_id [ns_session id]
			}
			array set sessiondata \
				[ns_cache_eval -expires $expires -- sessions $session_id {
					list session.id $session_id session.start [ns_time] session.state clean session.server unused
				}]
			return 1
		}

		save {
			# Save the session to the cache
			# Optional session_id for use in recovering sessions on startup
			if {[llength $args] == 0} {
				set session_id [ns_session id]
			} else {
				set session_id [lindex $args 0]
				set persist 0
			}
			upvar sessiondata sessiondata
			ns_cache_eval -expires $expires -force -- sessions $session_id {
				array get sessiondata
			}	
			if {$persist} {
				ns_session persist
			}
			return 1
		}

		delete {
			# Delete a given key
			ns_session load
			set key [lindex $args 0]
			array unset sessiondata $key
			ns_session save
			return 1

		}

		destroy {
			# Invalidate the session
			ns_cache_flush sessions [ns_session id] 
			ns_deletecookie session_id
		}
		
		persist {
			# Save the sesssion to the database
			upvar sessiondata sessiondata
			set session_id [ns_session id]
			set sessionserial [array get sessiondata]
			dbi_1row -db dbadminpg {SELECT fn_persistsession( 
				  :session_id::varchar,
				  :sessionserial::varchar) as result}
			return $result
		}

		recover {
			# Read all session info from database into cache
			# run on startup.
			foreach {sessionid sessiondataserial} [dbi_rows -db dbadminpg \
				{SELECT sessionid, sessiondata
				  FROM session
				  WHERE lastupdate > current_timestamp - '30 minutes'::interval}] {
				array set sessiondata $sessiondataserial
				ns_session save $sessionid
				array unset sessiondata
			}
		}

		get {
			# Get a value from the current session and return it
			set default ""
			set key [lindex $args 0]	
			if {[llength $args] == 2} {
				set default [lindex $args 1]
			}
			ns_session load
			ns_session touch
			if {[info exists sessiondata($key)]} {
				return $sessiondata($key)
			} else {
				return $default
			}
		}

		put {
			# Set a session variable to a given value
			set key [lindex $args 0]
			set value [lindex $args 1]
			ns_session load
			set sessiondata($key) $value
			ns_session save
		}

		contains {
			# Tell if the current session has a given key in it
			set key [lindex $args 0]
			ns_session load
			if {[info exists sessiondata($key)]} {
				return 1
			} else {
				return 0
			}
		}

		touch {
			# update the expiration by saving the session data
			# Not using "force" means this should never do anything
			# other than update the expiration.

			upvar sessiondata sessiondata
			if {$touch} {
				ns_cache_eval -force -expires $expires -- sessions [ns_session id]  {
					array get sessiondata
				}
			}
		}
	}
}	

ns_session recover

ns_register_proc GET /showsessions showsessions
proc showsessions {} {

	set html "your session id is [ns_session id]<br>"
	ns_session load
	append html "[array get sessiondata]"
	foreach key [ns_cache_stats -contents sessions] {
		foreach {size expiry} $key {
			append html "<BR>Size: $size Expires: [ns_fmttime [ns_time seconds $expiry]]"
		}
	}
	ns_return 200 text/html $html
}
